package top.humbleyuan.designmode.subpub;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * @Author HumbleYuan
 * @Date 2019/12/18 19:40
 * @Des 订阅发布器/平台, 相当于一个中间介质。
 * T为不定的msg类型
 */
public class SubPubPlatform<T> {
    // 平台名称
    private String name;

    // 平台的信息/通知容量
    private static final int QUEUE_CAPACITY = 20;

    // 平台信息存储队列
    private BlockingQueue<Msg> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);

    // 订阅者集合
    private List<ISubscriber> subscribers = new ArrayList<>();

    public SubPubPlatform(String name) {
        this.name = name;
    }

    /**
     * 发布者发布信息
     * @param publisher
     * @param msg
     * @param isInstantMsg 是否为即时发布
     */
    public void publish(String publisher, T msg, boolean isInstantMsg) {
        // 如果为实时信息，则立即通知
        if(isInstantMsg) {
            update(publisher, msg);
            return;
        }

        // 非实时信息，存入队列
        Msg<T> m = new Msg<>(publisher, msg);

        // 如果无法入队，将阻塞在队列中的所有通知全部释放通知
        // 如果可以入队则入队。即在队列满时将所有通知释放并进行通知
        if(!queue.offer(m))  {
            update();
        }

    }

    // 从队列中取出消息通知
    public void update(){
        Msg m = null;
        while ((queue.peek()) != null) {
            this.update(m.getPublisher(), (T) m.getMsg());
        }
    }

    public void update(String publisher, T msg) {
        // 为每一个订阅者发布信息
        for (ISubscriber sub:subscribers) {
            sub.update(publisher, msg);
        }
    }

    // 订阅者订阅
    public void sub(ISubscriber subscriber) {
        subscribers.add(subscriber);
    }

    // 订阅者退订
    public void unSub(ISubscriber subscriber) {
        if(!subscribers.isEmpty() && subscribers.contains(subscriber)) {
            subscribers.remove(subscriber);
        }
    }



    // 消息实体类,M为不定的msg类型
    class Msg<M> {
        private String publisher;   // 发布者
        private M m;    //发布消息

        public Msg(String publisher, M m) {
            this.publisher = publisher;
            this.m = m;
        }

        public String getPublisher() {
            return publisher;
        }

        public void setPublisher(String publisher) {
            this.publisher = publisher;
        }

        public M getMsg() {
            return m;
        }

        public void setMsg(M m) {
            this.m = m;
        }
    }

}
